package net.chrisrichardson.ec2deployer.core

import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import net.chrisrichardson.ec2deployer.cluster.EbsVolume
import java.sql.Connection
import java.sql.DriverManager
import net.chrisrichardson.ec2deployer.applications.SchemaSpecification
import java.util.concurrent.TimeUnit

// TODO rename this to Host to distinguish it from SoftwareServer???

class EC2Server {

    long id
    def owner
    def monitoredState
    
    Log logger = LogFactory.getLog(getClass())

    EC2InstanceState instanceState
    private SarMonitor sarMonitor
    CloudManager ec2

    EC2Server() {
    }

    EC2Server(CloudManager ec2, String instanceId, String imageId, String state, String availabilityZone) {
        this.ec2 = ec2
        this.instanceState = new EC2InstanceState(instanceId, imageId, state, availabilityZone)
    }

    CloudCommandExecutor getSsh() {
        ec2.ssh
    }

    AWSProperties getAwsProperties() {
        ec2.awsProperties
    }

    String getInstanceId() {
        instanceState.instanceId
    }
    
    String getState() {
        instanceState.state
    }

    String getPublicDnsName() {
        instanceState.publicDnsName
    }

    String getPrivateDnsName() {
        instanceState.privateDnsName
    }

    public updateStatus(newState, publicDnsName, privateDnsName) {
        instanceState.updateStatus newState, publicDnsName, privateDnsName
    }

    void waitUntilRunning() {
        instanceState.waitUntilRunning()
        waitUntilSshAvailable()
    }

    void waitUntilSshAvailable() {
        ssh.waitUntilSshAvailable(publicDnsName)
        assert isAvailable()
        executeConfigurationScript()
        executeScript(ec2.startupScript)
    }

    boolean isSshAvailable() {
       ssh.isSshAvailable(publicDnsName)
    }

    void executeScript(script) {
        if (script) {
            String scriptAsText = new File(script).text
            executeScriptText(scriptAsText);
        }
    }

    void executeScriptText(String scriptAsText) {
        ssh("dos2unix|bash", scriptAsText)
    }

    def executeConfigurationScript() {
        def script = ["ami-6f2cc906" : "/scripts/001install_aws.sh", "ami-0129cc68" : "/scripts/001install_aws.sh"][instanceState.imageId]
        if (script) {
           URL resource = getClass().getResource(script)
           executeScriptText (resource.text)
        }

    }

    public boolean isRunning() {
        instanceState.isRunning()
    }

    public boolean isTerminated() {
        instanceState.isTerminated()
    }

    public boolean isAvailable() {
        instanceState.isAvailable()
    }

    public void ssh(String command, String input = null, processMonitor = null) {
        ssh.ssh(publicDnsName, command, input, processMonitor)
    }

    public void scp(String fromFile, String toFile) {
        ssh.scpUp(publicDnsName, fromFile, toFile)
    }

    public void scpDown(String fromFile, String toFile) {
        ssh.scpDown(publicDnsName, fromFile, toFile)
    }

    def startSar() {
        getSarMonitor().start()
    }

    def stopSar() {
        sarMonitor.stop()
    }

    def getAverageBusy() {
        sarMonitor.getAverageBusy()
    }

    def stop() {
        ec2.stop(this)
    }

    def getSarMonitor() {
        if (!sarMonitor)
            sarMonitor = new SarMonitor(this)
        sarMonitor
    }

    def getAvailabilityZone() {
        instanceState.availabilityZone
    }

    boolean associateAddress(String externalIpAddress) {
      ec2.associateAddress this, externalIpAddress
    }

    void createEbsVolume(EbsVolume ebsVolume) {
        def params = ['Action': 'CreateVolume',
                      "AvailabilityZone" : availabilityZone,
                     ]
        if (ebsVolume.size)
            params += [ "Size": ebsVolume.size.toString() ]
        if (ebsVolume.snapshotId)
            params += [ "SnapshotId" : ebsVolume.snapshotId]
        def response = ec2.executeRequest(params)
        String volumeId = response.volumeId*.text()[0]
        ebsVolume.volumeId = volumeId

        for (int i in 1..20) {
            String status = ec2.getStatusOfEbsVolume(ebsVolume)
            if (status == "available") {
                println "Volume is available: $volumeId"
                return;
            }
            println "Sleeping for volume to be available: $i"
            TimeUnit.SECONDS.sleep(10)
        }
    }

    void attachEbsVolume(EbsVolume ebsVolume) {
        def params = ['Action': 'AttachVolume',
                      "VolumeId": ebsVolume.volumeId,
                      "InstanceId" : instanceId,
                      "Device" : ebsVolume.device
                     ]
        ec2.executeRequest(params)
    }

    String snapshotEbsVolume(EbsVolume ebsVolume, SchemaSpecification schemaSpec) {
        SnapshotProcessMonitor pm = new SnapshotProcessMonitor()
        ssh.ssh(publicDnsName, "", null, pm, "-L 4306:localhost:3306")

        TimeUnit.SECONDS.sleep 2
        
        try {
        	Class.forName("com.mysql.jdbc.Driver"); 
        } catch(Exception e){ 
	        throw new RuntimeException(e);
        }

        Connection dbConnection= DriverManager.getConnection("jdbc:mysql://localhost:4306/${schemaSpec.name}" , "root","");
        def statement = dbConnection.createStatement()
        statement.execute("FLUSH TABLES WITH READ LOCK")
        statement.execute("SHOW MASTER STATUS")

        ssh("xfs_freeze -f /vol")

        def params = ['Action': 'CreateSnapshot',
                "VolumeId": ebsVolume.volumeId
        ]
        def response = ec2.executeRequest(params)
        ssh("xfs_freeze -u /vol")
        statement.execute("UNLOCK TABLES")
        dbConnection.close()


        pm.stop()

        response.snapshotId*.text()[0]
    }


}